import random
import re
# 2.1 导入蓝图对象
from datetime import datetime

from flask import session

from info import constants, db
from info.lib.yuntongxun.sms import CCP
from info.models import User
from info.modules.passport import passport_blue
# 由全局的 request对象，属性headers 中的请求头，字典类型。
from flask import request, make_response, jsonify
# 导包  生成验证码，图片
from info.utils.captcha.captcha import captcha
# 保存导redis
from info import redis_store
# 请求上下文
from flask import current_app

from info.utils.response_code import RET


# 2.2使用蓝图对象实现路由

#  ---------------------------------------------------------------------------
# ⑤登出功能实现
@passport_blue.route("/logout", methods=['POST'])
def logout():
    """
    清除session中的对应登录之后保存的信息
    """
    session.pop('user_id', None)
    session.pop('nick_name', None)
    session.pop('mobile', None)

    # 返回结果
    return jsonify(errno=RET.OK, errmsg="OK")


#  ---------------------------------------------------------------------------
# ④用户登陆
# 请求方式：POST
# 请求路径：/login
# 参数：mobile,  password
@passport_blue.route('/login', methods=['POST'])
def login():
    """
    1.获取参数（两个）
    2.校验参数（完整性，手机号）
    3.判断数据库是否有该用户手机号
    4.判断密码正确性
    5.设置登陆
    6.返回登陆成功
    """
    # 1.获取参数（两个）
    json_dict = request.json
    mobile = json_dict.get('mobile')
    password = json_dict.get('password')

    # 2.校验参数（完整性，手机号）
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数不全')

    if not re.match('^1[3456789][1-9]{9}$', mobile):
        # 如果不匹配
        return jsonify(errno=RET.DATAERR, errmsg='手机号格式不正确')

    # 3.判断数据库是否有该用户手机号
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询mysql用户信息失败')

    # 判断用户名和密码错误 》》为了安全性，不需要分开判断
    if not user or not user.check_passowrd(password):
        return jsonify(errno=RET.DATAERR, errmsg='用户名或者密码错误')

    # 设置最后登陆的时间为当前时间
    user.last_login = datetime.now()

    # 提交回滚  在config中添加权限SQLALCHEMY_COMMIT_ON_TEARDOWN
    # try:
    #     db.session.commit()
    # except Exception as e:
    #     current_app.logger.error(e)
    #     db.session.rollback

    # 5.设置登陆 session
    session['user_id'] = user.id
    session['nickname '] = user.nick_name
    session['mobile'] = user.mobile

    # 6.返回登陆成功
    return jsonify(errno=RET.OK, errmsg='登陆成功')


#  ---------------------------------------------------------------------------
# ③注册用户
# 请求方式：POST
# 请求路径：/register
# 参数：mobile, sms_code, password
@passport_blue.route('/register', methods=['POST'])
def register():

    # 一。获取参数
    # 1.获取参数（三个） 封装成一个字典json_dict
    json_dict = request.json
    mobile = json_dict.get('mobile')
    sms_code = json_dict.get('sms_code')
    password = json_dict.get('password')

    # 二。校验参数
    # # 2.校验参数（完整性，手机号）
    if not all([mobile, sms_code, password]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数不全')

    if not re.match('1[3456789][0-9]{9}', mobile):
        return jsonify(erron=RET.DATAERR, errmsg='手机号格式不正确')

    # 三。逻辑处理
    # 3.从redis中获取短信验证码
    try:
        real_sms_code = redis_store.get('SMS_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(erron=RET.DBERR, errmsg='redis查询短信验证码数据失败')

    if not real_sms_code:
        return jsonify(erron=RET.NODATA, errmsg='短信验证码已过期或手机号码填写错误')

    # 4.对比验证码，对比失败返回信息
    if real_sms_code != sms_code:
        return jsonify(erron=RET.DATAERR, errmsg='短信验证码填写错误')

    # 5.对比成功，删除短信验证码
    try:
        redis_store.delete('SMS_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
        # 从用户体验角度来说，如果出错，可以不用返回json数据
        # return jsonify(erron=RET.DATAERR, errmsg='短信验证码填写错误')

    # 6.对比成功，注册用户（1。手机号是否注册过2.创建user对象3.添加到mysql数据库中)
    # 6.1 手机号是否注册
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(erron=RET.DBERR, errmsg='查询mysql的用户信息失败')

    if user:
        # 说明
        return jsonify(erron=RET.DATAEXIST, errmsg='手机号已注册')

    # 6.2 创建user对象
    user = User()
    user.nick_name = mobile
    user.mobile = mobile
    # 张娜）需要对密码做加密处理todo
    # 实际开发中，如果需要对模型数据进行处理，不会放在视图函数中进行，而是放在模型中进行
    # user.password_hash = password
    # 我们希望有个属性可以直接进行密码的加密处理，并赋值给password_hash属性
    user.password = password
    # print(user.password)
    # user.password = '123456'

    # 6.3添加到mysql数据库中
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        # 数据的修改操作，失败了需要回滚
        db.session.rollback()
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='添加mysql的用户信息失败')

    # 7.设置用户登陆 - session 存储可能用到的数据，不能存密码
    session['user_id'] = user.id
    session['nickname'] = user.nick_name
    session['mobile '] = user.mobile

    # 四。返回数据
    return jsonify(errno=RET.OK, errmsg='用户注册成功')


#  ---------------------------------------------------------------------------
# ②获取短信验证码
# 请求方式：POST
# 请求路径：/sms_code?mobile=13*********&image_code=abd&image_code_id=******
# 参数：mobile, image_code, image_code_id
@passport_blue.route('/sms_code', methods=['POST'])
def get_sms_code():
    # 一.获取参数
    # 1.获取参数（手机号，图像验证码内容，图像验证码ID）
    # 局部数据更新需要使用json来传递数据
    # request.data返回的是字符串数据
    # json.loads(request.data) ['key']

    json_data = request.json
    # print(json_data)
    # print(request.json)
    mobile = json_data.get('mobile')
    image_code = json_data.get('image_code')
    image_code_id = json_data.get('image_code_id')

    # 二。校验参数（完整性，手机号）
    # 2.校验数据（完整性/正则验证手机号）
    if not all([mobile, image_code, image_code_id]):
        # 如果参数不全，会进入该分支
        return jsonify(erron=RET.PARAMERR, errmsg='参数不全')

    if not re.match("^1[3456789][0-9]{9}$", mobile):
        # 如果手机号不匹配
        return jsonify(erron=RET.DATAERR, errmsg='手机号码格式不正确')

    # 三。逻辑处理
    # 3.获取redis的图像验证码数据
    try:
        real_image_code = redis_store.get('ImageCodeID'+image_code_id)
    except Exception as e:
        current_app.logging.error(e)
        return jsonify(erron=RET.DBERR, errmsg='redis图像验证码查询失败')

    # 查询mysql，redis，都需要做判空处理，验证码是否过期
    if not real_image_code:
        # 验证码已过期
        return jsonify(erron=RET.NODATA, errmsg='手机号验证码已过期')

    # 4.对比两个数据数据，对比失败就返回json数据
    # 4.1 为了完善逻辑，图像验证码从redis获取之后就删除
    try:
        redis_store.delete('ImageCodeID'+image_code_id)
    except Exception as e:
        current_app.logging.error(e)
        # 可删可不删
        return jsonify(erron=RET.DBERR, errmsg='redis删除失败')

    # 4.2 对比redis和用户传入的数据
    # ABC 》abc
    if real_image_code.lower() != image_code.lower():
        # 对比失败，返回json数据
        return jsonify(erron=RET.DATAERR, errmsg='手机验证码错误')

    # 4.3 图像验证码对比成功，再看手机号是否注册过
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logging.error(e)
        return jsonify(erron=RET.DBERR, errmsg='查询mysql的用户信息失败')

    if user:
        # 说明用户已注册
        return jsonify(erron=RET.DATAEXIST, errmsg='手机号已注册')

    # 5.对比成功，生成短信验证
    # '%06d':数字是6位数，不足以0补齐
    sms_code_str = '%06d' % random.randint(0, 999999)
    print(sms_code_str)

    # 6.保存redis
    try:
        redis_store.set('SMS_' + mobile, sms_code_str, constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        # 保存短信验证码失败
        return jsonify(errno=RET.DBERR, errmsg="保存短信验证码失败")

    # 6.调用第三方,云通讯发短信验证码
    # result = CCP().send_template_sms(mobile, [sms_code_str, constants.SMS_CODE_EXPIRES], 1)
    # if result != 0:
    #     return jsonify(errno=RET.THIRDERR, errmsg='发送短信验证码失败')

    # 四 返回数据
    return jsonify(errno=RET.OK, errmsg='发送短信验证码成功')


#  ---------------------------------------------------------------------------
# ①获取图像验证码
# 请求方式：GET
# 请求路径：/image_code
# 参数：image_code_id
@passport_blue.route('/image_code')
def get_image_code():
    # 'http://127.0.0.1:5000/passport/image_code?image_code_id=eb49bcc9-1030-4289-81c2-dec95fe74bdd'
    # 1.获取参数
    image_code_id = request.args.get('image_code_id')

    # 2.调用工具类生成验证码，图片
    # name, text, out.getvalue()
    name, text, image_data = captcha.generate_captcha()
    # print(name)
    # print(text)
    # print(image_data)

    # 3.保存到redis中  try  ①在__init__加备注类型
    try:
        # 可以给redis增加类型注释来查看
        redis_store.set('ImageCodeID'+image_code_id, text, constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        # 保存日志
        current_app.logging.error(e)
        # 返回错误信息 》JSON格式
        # 如果要全局更新网页，可以渲染模板
        # 如果只是局部更新网页，前后端只需要使用json传输即可
        # 返回一个字典格式的字符串"{'errno':4001, 'errmsg':'保存redis出错'}"
        return jsonify(errno=RET.DBERR, errmsg='保存redis出错')

    # 4.返回图像
    response = make_response(image_data)
    response.headers['Content-type'] = 'image/jpg'
    return response





